/**
 * Copyright 2002 Cordys R&D B.V. 
 * 
 * This file is part of the Cordys .NET Connector. 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 /**********************************************************************
  Definition of Document Class and Global functions which will be 
  invoked from the managed functions. 
	Xml(function)name is the naming convention for the managed funtions
	which are the helper functions for the Class member functions

**********************************************************************/
#include "xmlForDotnet.h"
#include "Unmanaged.h"
#include "Managed.h"
#include "Document.h"

using namespace com::eibus::xml::nom;

/*****************************************************************************************
	The following functions are managed Functions which are used to helper functions for
	the Document Class functions. For making the data that is allocated on the CLR's heap
	available for the Unmanaged layer , the data is pinned. Pinning of the Data means ,
	the data that is allocated on CLR's heap should not be collected when GC is performing 
	garbage collection.And by releasing the pinned pointer, we are allowing the GC to 
	perform the garbage collection on this data also.
*****************************************************************************************/


/*****************************************************************************************
	#pragma managed is directive that should be used for making this function as an
	managed function. for defining the Unmanaged function " #pragma unmanaged " should 
	be used. until the aleternative directive is defined, the current directive applies
	to all the functions that are followed by this directive.
*****************************************************************************************/
#pragma managed

int CreateDocument()
{
	 return UMCreateDocument();
}

/*
 * Invokes the unmanaged API for reading the bytes and returns the 
 * xml root reference.
 */
Int32 XmlReadBytes(Int32 doc_handle, Byte bytes[] ,Int32 sizeOfBytes)
{
	Byte __pin * pbytes = &bytes[0];
	try
	{	
		return UMReadBytes(doc_handle, pbytes ,sizeOfBytes);

	}catch(const char* exString)
	{
		throw new XMLException(new String(exString));
	}
	__finally
	{
		ReleasePINPointer(pbytes); 
	}
}

Int32 XmlReadFromFile(Int32 doc_handle,String __gc * fileName)
{
	//Get the bytes of string in UTF -8 format.
	//To convert the managed pointer to unmanaged pointer
	// Releasing of this 
	Byte __pin * pFileName = GetUTF8CharForStr(fileName);	

	try
	{
		return UMReadFromFile(doc_handle, (unsigned char*)pFileName);

	}catch(const char* exString)
	{
		throw new XMLException(new String(exString));
	}
	__finally
	{
		ReleasePINPointer(pFileName); 
	}
}

/*
 * This method takes the document handle and the filename from which 
 * the xml should read.
 */
Int32 XmlParseString(Int32 doc_handle, String __gc * xmlString)
{	
	Byte __pin * pbytes = GetUTF8CharForStr(xmlString);
	Int32 xmlRoot = -1;
	try
	{	
		Int32 sizeOfBytes = GetByteLength(xmlString);
		xmlRoot = UMReadBytes(doc_handle, pbytes ,sizeOfBytes);
		return xmlRoot;
	}
	catch(const char *exString)
	{
		throw new XMLException(new String(exString));
	}
	__finally
	{
		ReleasePINPointer(pbytes);
	}
	return xmlRoot;
}

Int32 XmlCreateElement(Int32 doc_handle , String __gc * elementName , Int32 parentElement)
{
	Byte __pin * pbytes = GetUTF8CharForStr(elementName);
	Int32 xmlNodeID = -1;

	xmlNodeID = UMCreateElementNode(doc_handle, pbytes, parentElement);
	ReleasePINPointer(pbytes);
	return xmlNodeID;
}

// New for BP
Int32 XmlCreateElementWithParentNS(Int32 doc_handle , String __gc * elementName , Int32 parentElement)
{
	Byte __pin * pbytes = GetUTF8CharForStr(elementName);
	Int32 xmlNodeID = -1;

	xmlNodeID = UMCreateElementNodeWithParentNS(doc_handle, pbytes, parentElement);
	ReleasePINPointer(pbytes);
	return xmlNodeID;
}

Int32 XmlCreateDataNode(Int32 doc_handle , String __gc * textStr, Int32 parentNode)
{
	Byte __pin * pbytes = GetUTF8CharForStr(textStr);

	Int32 xmlNodeID = -1;

	xmlNodeID = UMCreateDataNode(doc_handle, pbytes, parentNode);
	ReleasePINPointer(pbytes);
	return xmlNodeID;
}

Int32 XmlCreateDataElement(Int32 doc_handle , String __gc * element , String __gc * textStr, Int32 parentNode)
{
	Byte __pin * pbytes = GetUTF8CharForStr(textStr);
	Byte __pin * elementBytes = GetUTF8CharForStr(element);
	Int32 xmlNodeID = -1;

	xmlNodeID = UMCreateDataElement(doc_handle, elementBytes, pbytes, parentNode);
	ReleasePINPointer(pbytes);
	ReleasePINPointer(elementBytes);
	return xmlNodeID;
}

// New for BP
Int32 XmlCreateDataElementWithParentNS(Int32 doc_handle , String __gc * element , String __gc * textStr, Int32 parentNode)
{
	Byte __pin * pbytes = GetUTF8CharForStr(textStr);
	Byte __pin * elementBytes = GetUTF8CharForStr(element);
	Int32 xmlNodeID = -1;

	xmlNodeID = UMCreateDataElementWithParentNS(doc_handle, elementBytes, pbytes, parentNode);
	ReleasePINPointer(pbytes);
	ReleasePINPointer(elementBytes);
	return xmlNodeID;
}

void LockDoc(Int32 doc_handle)
{
	UMLockDoc(doc_handle); 
}


void UnLockDoc(Int32 doc_handle)
{
	UMUnLockDoc(doc_handle); 
}


Int32 IncreaseRefCount(Int32 doc_handle)
{
	// increases reference counter for the document
	return (int) xmlDocReference(getXmlDoc(doc_handle),1);
}

void DeleteDocument(int doc_handle)
{
	// Decreases reference counter and deletes document if no
	// more references to the document exist.
	if (xmlDocReference(getXmlDoc(doc_handle), -1)<=0)
	{
		xmlEndDoc(getXmlDoc(doc_handle));
		deleteXmlDocId(doc_handle);
	}
}


/****************************************************************************************
					Function Implementation of the Document class
****************************************************************************************/

/* Creates a new document. This will also allocate native memory space. That
 * memory is freed when the method finalize() is called.
 */
Document::Document()
	{
		doc_handler = CreateDocument();		
	}


/*
 * Constructor which takes the Document handler and assigns it the internal reference and 
 * increases the reference count.
 */
Document::Document(Int32 doc_handle)
		{	
			doc_handler = doc_handle;
            IncreaseRefCount(doc_handler);
		}

/*
 * Returns the handle to the native context of the Document.
 */
Int32 Document::GetHandle()
		{
			return doc_handler;
		}

/*
 * This method should only be called by the CLR. It frees the memory
 * that has been allocated by native functions.
 */
void Document::!Document()
		{
			DeleteDocument(doc_handler);
		}

/*
 *  Loads the byte array and prepares the document 
 *  and returns the pointer reference to the root.
 *  Zero is returned if the byte array is null.
 */
Int32 Document::Load(Byte bytes[] ,Int32 length)
		{	
			if(bytes!=NULL)
			{
				return XmlReadBytes(doc_handler,bytes,length);									
			}
			else
			{
				return 0;
			}
		}

/*
 * Loads the byte array and prepares the document and 
 * returns the reference to the root node.
 */
Int32 Document::Load(Byte bytes[])
		{	
			return Load(bytes,bytes->Length);
		}

/*
 * Load Method takes the filename as argument and reads the file 
 * and prepares the document and returns the reference to the 
 * root element.It throws XMLException when any error occurs during 
 * the reading and loading the document.
 */
Int32 Document::Load(String*  fileName)
		{
			return XmlReadFromFile(doc_handler,fileName);
		}

Int32 Document::ParseString(String* xmlString)
			{
				return XmlParseString(doc_handler,xmlString);
			}

/*
/*
 * Check this for encoding support
 *
Int32 Document::ParseString(String* xml, String* encoding)
			{
				try
				{
					//Currently it supports only UTF-8 so no need to 
					//Consider the argument 'encoding' right now.
					return XmlParseString(doc_handler,xml);
				}
				catch(const char* xmlException)
				{
				throw xmlException;
				}
			}
*/

/** Creates a new element and appends it to the children of the indicated
 * parent.
 * @param tag The tagname for the new element
 * @param parent The parent for the new element
 */
Int32 Document::CreateElement(String* elementName, Int32 parentElement)
			{
				return XmlCreateElement(doc_handler , elementName , parentElement);
			}

/** Creates a new element and appends it to the children of the indicated
 * parent. The new element will have the parent's namespace.
 * @param tag The tagname for the new element
 * @param parent The parent for the new element
 */
// New for BP
Int32 Document::CreateElementWithParentNS(String* elementName, Int32 parentElement)
	{
		return XmlCreateElementWithParentNS(doc_handler , elementName , parentElement);
	}

/** Creates a new element 
 * @param tag The tagname for the new element 
 */
Int32 Document::CreateElement(String* elementName)
		{	
			return XmlCreateElement(doc_handler , elementName ,0);
		}

/** Creates a new text node and appends it to the children of the indicated
	* parent.
	* @param text The data for the new node
	* @param parent The parent for the new node
	*/
Int32 Document::CreateText(String* textElementString,int parentElement)
		{	
			return XmlCreateDataNode(doc_handler, textElementString, parentElement);
		}

/** Creates a new text node.
	* @param text The data for the new node
	*/
Int32 Document::CreateText(String* textElementString)
		{
			return XmlCreateDataNode(doc_handler, textElementString, 0);
		}

/** Creates a new element, appends it to parent, and creates a new text node 
	* which is appended to the newly created element.
	* @param tag The tag for the new element
	* @param text The data for the new node
	* @param parent The parent for the new element
	*/
Int32 Document::CreateTextElement(String* textElementString , String*  elementValue, int parentElement)
		{
			return XmlCreateDataElement(doc_handler, textElementString , elementValue, parentElement);
		}

/** Creates a new element, appends it to parent, and creates a new text node 
	* which is appended to the newly created element.
	* @param tag The tag for the new element
	* @param text The data for the new node
	* @param parent The parent for the new element
	*/
// New for BP
Int32 Document::CreateTextElementWithParentNS(String* textElementString , String*  elementValue, int parentElement)
		{
			return XmlCreateDataElementWithParentNS(doc_handler, textElementString , elementValue, parentElement);
		}

/** Creates a new element and creates a new text node 
	* which is appended to the newly created element.
	* @param tag The tag for the new element
	* @param text The data for the new node
	*/
Int32 Document::CreateTextElement(String* textElementString , String* elementValue)
		{
			return XmlCreateDataElement(doc_handler, textElementString , elementValue, 0);
		}

/** Locks the document for other Threads.
	*/
void Document::LockDocument()
		{
			return LockDoc(doc_handler);
		}

/** Unlocks the document for other Threads.
	*/
void Document::UnLockDocument()
		{
			return UnLockDoc(doc_handler);
		}